﻿using System.Linq;
using CID.Domain.Development;
using CID.Domain.Exceptions;
using CID.Domain.Infrastructure;
using CID.Tests.Support;
using NUnit.Framework;
using CID.Domain.Operations;
using CID.Domain.Users;

namespace CID.Domain.Tests.Development
{
    [TestFixture]
    class ComponentTests : BaseFunctionalTests
    {
        private const string Name = "Component Name";
        private const string Description  = "Component Description";
        private static readonly IServerRole RequiredServerRole = Operations.StubsBuilder.BuildServerRoleStub();
        private static readonly IUser Responsible = Users.StubsBuilder.BuildUserStub();

        private Component component;
        public override void SetUp()
        {
            base.SetUp();
            component = new Component(Name, Description, RequiredServerRole, Responsible);
        }

        #region Constructor
        [Test]
        public void CreateComponent_ShouldSucceed()
        {
            var component = new Component(Name, Description, RequiredServerRole, Responsible);

            Assert.AreEqual(Name, component.Name);
            Assert.AreEqual(Description, component.Description);
            Assert.AreEqual(RequiredServerRole, component.RequiredServerRole);
            Assert.AreEqual(Responsible, component.Responsible);
            Assert.IsTrue(component.Enabled);
            Assert.IsInstanceOf<ActivationDeactivationDefinition>(component.ActivationDeactivationDefinition);
            Assert.AreEqual(component, component.ActivationDeactivationDefinition.Component);
            CollectionAssert.IsEmpty(component.Dependants);
            CollectionAssert.IsEmpty(component.Versions);
        }
        #endregion

        #region AddVersion
        [Test]
        public void CreateVersionOnce_ShouldSuceed()
        {
            var version = component.CreateVersion(VersionTests.Name, VersionTests.Description);
            
            Assert.AreEqual(1, component.Versions.Count());
            CollectionAssert.Contains(component.Versions, version);
            VersionTests.AssertVersion(version, component);
        }

        [Test, ExpectedException(typeof(DuplicateChildException))]
        public void CreateVersionTwice_ShouldThrowException()
        {
            component.CreateVersion(VersionTests.Name, VersionTests.Description);
            component.CreateVersion(VersionTests.Name, VersionTests.Description);
        }
        #endregion

        #region RemoveVersion
        [Test]
        public void RemoveVersion_VersionFromAnotherComponent_NothingShouldBeDone()
        {
            var version = StubsBuilder.BuildVersionStub(component);
            component.AddVersion(version);
            var anotherComponent = new Component(Name, Description, RequiredServerRole, Responsible);
            var anotherVersion = StubsBuilder.BuildVersionStub(anotherComponent);
            anotherComponent.AddVersion(anotherVersion);

            component.RemoveVersion(anotherVersion);

            Assert.AreEqual(1, component.Versions.Count());
        }

        [Test]
        public void RemoveVersion_VersionFromTheComponentAndNoReleaseAssigned_ShouldSucceed()
        {
            var version = StubsBuilder.BuildVersionStub(component);
            component.AddVersion(version);

            component.RemoveVersion(version);

            Assert.AreEqual(0, component.Versions.Count());
        }

        [Test]
        public void RemoveVersion_VersionFromTheComponentAndNotReleasedReleaseAssigned_ShouldSucceed()
        {
            var release = StubsBuilder.BuildReleaseStub(ReleaseStatus.Planned);
            var version = StubsBuilder.BuildVersionStub(component, release);
            component.AddVersion(version);

            component.RemoveVersion(version);

            Assert.AreEqual(0, component.Versions.Count());
        }

        [Test,ExpectedException(typeof(InvalidActionForReleaseStatusException))]
        public void RemoveVersion_VersionFromTheComponentAndReleasedReleaseAssigned_ShouldThrowException()
        {
            var release = StubsBuilder.BuildReleaseStub(ReleaseStatus.Released);
            var version = StubsBuilder.BuildVersionStub(component, release);
            component.AddVersion(version);

            component.RemoveVersion(version);
        }
        #endregion

        #region AddDependant
        [Test]
        public void AddDependantOnce_OneDependantShouldBeAdded()
        {
            IVersion version = StubsBuilder.BuildVersionStub();

            component.AddDependant(version);

            Assert.AreEqual(1, component.Dependants.Count());
            CollectionAssert.Contains(component.Dependants, version);
        }

        [Test]
        public void AddDependantTwice_OneDependantShouldBeAdded()
        {
            IVersion version = StubsBuilder.BuildVersionStub();

            component.AddDependant(version);
            component.AddDependant(version);

            Assert.AreEqual(1, component.Dependants.Count());
        }
        #endregion

        #region RemoveDependant
        [Test]
        public void RemoveDependant_TwoDependantsExists_OneDependantShouldRemain()
        {
            IVersion version1 = StubsBuilder.BuildVersionStub();
            IVersion version2 = StubsBuilder.BuildVersionStub();
            component.AddDependant(version1); 
            component.AddDependant(version2);

            component.RemoveDependant(version1);

            Assert.AreEqual(1, component.Dependants.Count());
            CollectionAssert.DoesNotContain(component.Dependants, version1);
            CollectionAssert.Contains(component.Dependants, version2);
        }

        [Test]
        public void RemoveDependantTwice_TwoDependantsExists_OneDependantShouldRemain()
        {
            IVersion version1 = StubsBuilder.BuildVersionStub();
            IVersion version2 = StubsBuilder.BuildVersionStub();
            component.AddDependant(version1);
            component.AddDependant(version2);

            component.RemoveDependant(version1);
            component.RemoveDependant(version1);

            Assert.AreEqual(1, component.Dependants.Count());
            CollectionAssert.DoesNotContain(component.Dependants, version1);
            CollectionAssert.Contains(component.Dependants, version2);
        }
        #endregion
    }
}
